/* eslint-disable no-console */

import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
// import { floodsub } from '@libp2p/floodsub'
// import { gossipsub } from '@chainsafe/libp2p-gossipsub'
import { gossipsub } from '../gossipsub/index.js'
import { mplex } from '@libp2p/mplex'
import { tcp } from '@libp2p/tcp'
import { webSockets } from '@libp2p/websockets'
import { createLibp2p } from 'libp2p'
import { identifyService } from 'libp2p/identify'
import { preSharedKey } from 'libp2p/pnet'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import pMap from 'p-map';
import { peerIdToDid } from '../peer-id.js'
import { multiaddr } from '@multiformats/multiaddr'
import { floodsub } from '@libp2p/floodsub'
import Logger from 'logplease'

const logger = Logger.create('client')
Logger.setLogLevel('DEBUG')

const swarmKey_dev = '/key/swarm/psk/1.0.0/\n/base16/\n' + '7ba72b5b15148919d364693b0a8cd2c36a09a3345f210c96ccc019e2ace1def6'
const swarmKey = uint8ArrayFromString(swarmKey_dev)

const createNode = async (name) => {
  const node = await createLibp2p({
    // addresses: {
    //   listen: ['/ip4/0.0.0.0/tcp/0']
    // },
    transports: [webSockets()],
    streamMuxers: [yamux(), mplex()],
    connectionEncryption: [noise()],
    services: {
      // pubsub: gossipsub({ emitSelf: false, allowPublishToZeroPeers: true,  }),
      pubsub: floodsub(),
      identify: identifyService()
    },
    // connectionProtector: preSharedKey({
    //   psk: swarmKey
    // })
    // connectionManager: {
    //   minConnections: 2,
    //   maxConnections: 10
    // }
  })

  logger.debug(`${name}: started with id ${node.peerId.toString()}`)
  // logger.debug('Listening on:')
  // node.getMultiaddrs().forEach((ma) => logger.debug(ma.toString()))

  // node.services.pubsub.subscribe(topic)
  // node.services.pubsub.addEventListener('message', (evt) => {
  //   logger.debug(`${name} received: ${uint8ArrayToString(evt.detail.data)} on topic ${evt.detail.topic}`)
  // })

  return node
}

const sleep = (maxTimeout) => {
  const rand = Math.random()
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve()
    }, maxTimeout*rand)
  })
}

const waitForChannelReady = async (node, did, name) => {
  node.services.pubsub.subscribe(`${did}-channel`)
  let waitIntervalId_2
  const waitChannelReady = () => {
    return new Promise((resolve, reject) => {
      logger.debug(`${name}: wait for channal is ready...`)
      waitIntervalId_2 = setInterval(() => {
        node.services.pubsub.subscribe(`${did}-channel`)
        const peers = node.services.pubsub.getSubscribers(`${did}-channel`)
        // logger.debug(`${name}: channel peers`, peers.map(item => item.toString()))
        if (peers.length > 0) {
          logger.debug(`${name}: channel is ready!!!!!`)
          resolve()
        }
      }, 1000)
    })
  }
  await waitChannelReady()
  clearInterval(waitIntervalId_2)
}

const createClient = async (name, serverAddress, maxTimeout) => {
  const node = await createNode(name)
  let peerId = node.peerId.toString()
  let did = peerIdToDid(peerId)

  await node.dial(multiaddr(serverAddress))
  logger.debug('connected to server')
  logger.debug('subscribe channel: ', `${did}-channel`)
  node.services.pubsub.subscribe(`${did}-channel`)

  node.addEventListener('peer:disconnect', (evt) => {
    let peerId = evt.detail.toString()

    logger.debug(`Disconnected from libp2p peer ${peerId}`)

  })

  node.services.pubsub.addEventListener('message', async (evt) => {
    const topic = evt.detail.topic
    const from = evt.detail.from.toString()
    const msg = uint8ArrayToString(evt.detail.data)
    const body = JSON.parse(msg)
    logger.debug(`${name} received: ${msg} from ${body.name}, topic: ${topic}`)

    if (topic.endsWith('-channel')) {
      const channelDid = topic.split('-')[0]
      if (body.type == 'start-heart-beat') {
        logger.debug('start heart beat')
        setInterval(() => {
          logger.debug(`${name}: send heart beat`)
          node.services.pubsub.publish(`${did}-channel`, new TextEncoder().encode(JSON.stringify({ type: 'heart-beat', name, time: Date.now() })));
        }, 500)
      }
    }
  })

  // setInterval(() => {
  //   logger.debug(`${name}: send heart beat`)
  //   node.services.pubsub.publish(`${did}-channel`, new TextEncoder().encode(JSON.stringify({ type: 'heart-beat', name, time: Date.now() })));
  // }, 500)
  
  // await waitForChannelReady(node, did, name)

  // while(true) {
  //   await sleep(maxTimeout)
  //   logger.debug(`${name}: send heart beat`)
  //   node.services.pubsub.publish(`${did}-channel`, new TextEncoder().encode(JSON.stringify({ type: 'heart-beat', name })));
  // }

}

const sleep2 = (timeout) => {
  const rand = Math.random()
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve()
    }, timeout)
  })
}


;(async () => {
  const serverAddress = '/ip4/127.0.0.1/tcp/4008/ws/p2p/12D3KooWELrypWhpyUef9gtJr5TUne1173fG24QpMrRN62HN4VDk'
  // const serverAddress = '/dns4/libp.we-linkin.com/tcp/44008/wss/p2p/12D3KooWELrypWhpyUef9gtJr5TUne1173fG24QpMrRN62HN4VDk'

  const range = []
  const count = 400
  for (let i = 0; i < count; i++) range.push(i+1)

  // const mapper = async (i) => {
  //   await sleep(180000)
  //   createClient('client_' + i, serverAddress, 30000)
  // }
  // pMap(range, mapper)

  for (let i = 0; i < 400; i++) {
    await createClient('client_' + (i + 1), serverAddress)
    await sleep(1000)
  }

})()
